﻿using System;
using Pfz.Factoring;

namespace Pfz.WpfControls
{
	/// <summary>
	/// Class that controls how editors are shown in the application.
	/// </summary>
	public static class Editor
	{
		static Editor()
		{
			Editing = (args) =>
			{
				var window = new WindowWithResult();
				window.Title = "Editing";

				args.Editor.Record = args.Record;
				window.Content = args.Editor;

				using
				(
					new ResultSetter
					(
						(result) => 
						{
							window.Result = result;
							window.DialogResult = true;
						}
					)
				)
				{
					window.ShowDialog();
				}

				return window.Result;
			};
		}

		/// <summary>
		/// Edits the Record set to it. Waits until the record is edited.
		/// </summary>
		/// <returns></returns>
		public static T Edit<T>(T record)
		{
			if (record == null)
				throw new ArgumentNullException("record");

			var handler = _threadEditing;
			if (handler == null)
			{
				handler = Editing;

				if (handler == null)
					throw new InvalidOperationException("The Editing and ThreadEditing handlers are not set.");
			}

			IFactory<IEditor> factory;
			Type recordType = record.GetType();
			if(recordType == typeof(T))
				factory = Factories<IEditor>.CreateFactory(typeof(T));
			else
			{
				var factory1 = Factories<IEditor>.TryCreateFactory(recordType);
				var factory2 = Factories<IEditor>.TryCreateFactory(typeof(T));

				if (factory1 == factory2)
				{
					if (factory1 == null)
						throw new InvalidOperationException("Can't find a factory for " + record.GetType());

					factory = factory1;
				}
				else
				{
					if (factory1 == null)
						factory = factory2;
					else
					if (factory2 == null)
						factory = factory1;
					else
					if (factory1.BaseDataType.IsAssignableFrom(factory2.BaseDataType))
						factory = factory2;
					else
						factory = factory1;
				}
			}

			var control = factory.Create();

			EditorArgs args = new EditorArgs();
			args.Editor = control;
			args.Record = record;
			var result = handler(args);
			return (T)result;
		}

		/// <summary>
		/// Gets or sets the function used to edit records for the entire application.
		/// </summary>
		public static Func<EditorArgs, object> Editing { get; set; }

		[ThreadStatic]
		internal static Func<EditorArgs, object> _threadEditing;
	}
}
